#include <QThread>
#include <QDebug>
#include <QSerialPort>
#include <QSerialPortInfo>
#include "serialthread.h"

enum {
    NBStateInit = 0,
    NBStateGetCCID,
    NBStateGetIMEI,
    NBStateClosePSM,
    NBStateCloseERDX,
    NBStateClosePM,
    NBStateSetCOPS,
    NBStateSetCGDCONT,
    NBStateSetCGACT,
    NBStateBind,
    NBStateSend
};

SerialThread::SerialThread(QObject *parent) :
    QObject(parent),
    m_NBState(NBStateInit),
    recvNum(0),
    sndNum(0),
    m_PortInfo(nullptr)
{
    m_IsRun = false;
    m_IsStart = false;
    m_IsOpen = false;

    m_Port = new QSerialPort();
    m_Thread = new QThread();
    m_Timer = new QTimer();

    qDebug() << "SerialThread constructor id: " << QThread::currentThreadId();
    this->moveToThread(m_Thread);
    m_Timer->moveToThread(m_Thread);
    m_Port->moveToThread(m_Thread);

    connect(m_Thread, &QThread::finished, m_Thread, &QThread::deleteLater);
    connect(this, &SerialThread::SignalOpen, this, &SerialThread::SlotOpenSerailPort);
    connect(this, &SerialThread::SignalClose, this, &SerialThread::SlotCloseSerialPort);
    connect(this, &SerialThread::SignalSend, this, &SerialThread::SlotSend);

    //子线程使用timer必须使用Qt::DirectConnection
    m_Timer->setInterval(6000);
    //connect(m_Thread, &QThread::started, m_Timer, &QTimer::start);
    connect(m_Thread, SIGNAL(started()), m_Timer, SLOT(start()));
    connect(m_Thread, &QThread::finished, m_Timer, &QTimer::stop);
    connect(m_Timer, &QTimer::timeout, this, &SerialThread::SlotTimer);
    //m_Timer->start();

    //default param
    m_Port->setBaudRate(QSerialPort::Baud9600);
    m_Port->setDataBits(QSerialPort::Data8);
    m_Port->setParity(QSerialPort::NoParity);
    m_Port->setStopBits(QSerialPort::OneStop);
    m_Port->setFlowControl(QSerialPort::NoFlowControl);
    connect(m_Port, &QSerialPort::readyRead, this, &SerialThread::SlotRead);

    m_Thread->start();
}

SerialThread::~SerialThread()
{
    m_IsRun = false;
    //m_Timer->stop();

    if(m_Thread->isRunning()) {
        //m_Thread->requestInterruption();
        m_Thread->quit();
        m_Thread->wait();
    }

    //线程退出后再disconnect
    disconnect(m_Timer, &QTimer::timeout, this, &SerialThread::SlotTimer);
    disconnect(m_Port, &QSerialPort::readyRead, this, &SerialThread::SlotRead);
    disconnect(this, &SerialThread::SignalOpen, this, &SerialThread::SlotOpenSerailPort);
    disconnect(this, &SerialThread::SignalClose, this, &SerialThread::SlotCloseSerialPort);
    disconnect(this, &SerialThread::SignalSend, this, &SerialThread::SlotSend);

    delete m_Timer;
    delete m_Port;
    delete m_Thread;

    qDebug() << "SerialThread Destroy!";
}

void SerialThread::SetRun(bool flag)
{
    std::lock_guard<std::mutex> lock(m_Mutex);

    m_IsRun = flag;
}

void SerialThread::SlotRecvCircle()
{
    if(m_IsStart) return;

    m_IsStart = true;
    qDebug() << "SerialThread id: " << QThread::currentThreadId();
    emit SignalInfo(u8"开始串口线程接收循环");

    while(1)
    {
        if(QThread::currentThread()->isInterruptionRequested()) break;
    }
}

void SerialThread::SlotTimer()
{
    static unsigned char cnt = 0;

    if(!m_IsOpen || !m_IsRun) return;

    if(m_IsRun && m_Port->isOpen()) {
        switch(m_NBState)
        {
        case NBStateInit:
            cnt = 0;
            SendATCmd("ATE0\r\n");
            //SendATCmd("AT\r\n");
            break;
        case NBStateGetCCID:
            SendATCmd("AT+CCID\r\n");
            break;
        case NBStateGetIMEI:
            SendATCmd("AT+CGSN\r\n");
            break;
        case NBStateClosePSM:
            SendATCmd("AT+CPSMS=0\r\n");
            break;
        case NBStateCloseERDX:
            SendATCmd("AT+CEDRXS=0\r\n");
            break;
        case NBStateClosePM:
            SendATCmd("AT+NVSETPM=0\r\n");
            break;
        case NBStateSetCOPS:
            SendATCmd("AT+COPS=0\r\n");
            break;
        case NBStateSetCGDCONT:
            SendATCmd("AT+CGDCONT=1,\"IP\",\"ctnb\"\r\n");
            break;
        case NBStateSetCGACT:
            SendATCmd("AT+CGACT=1,1\r\n");
            break;
        case NBStateBind:
            if(cnt / 4 == 0) {
                SendATCmd("AT+NCDPOPEN=\"180.101.147.115\"\r\n");
            }
            ++cnt;
            break;
        case NBStateSend:
            SendATCmd("AT+CSQ\r\n");
            QThread::msleep(5);
            SendATCmd("AT+NMGS=7,11010400000064\r\n");
            emit SignalUpDown(true);
            break;
        }
    }
    else {
        if(m_Port->isOpen())
            m_Port->clear(QSerialPort::AllDirections);
    }
}

void SerialThread::SlotRead()
{
    static QString readBuf;

    QByteArray readByte = m_Port->readAll();

    if(readByte.isEmpty()) return;

    emit SignalInfo(readByte);

    QString tb = QString(readByte);
    recvNum += readByte.length();

    readBuf.append(tb);

    int buflen = readBuf.length();
    if(buflen < 6) return;

    int rlen = 0;
    switch(m_NBState)
    {
    case NBStateInit:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateGetCCID;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;

    case NBStateGetCCID:
        rlen = readBuf.indexOf("+CCID: \"");
        if(buflen - rlen < 28) break;

        if(rlen >= 0) {
            m_NBState = NBStateGetIMEI;
            QString ccid = readBuf.mid(rlen+8, 20);
            emit SignalCCID(ccid);
        }
        readBuf.clear();
        break;

    case NBStateGetIMEI:
        {
            if(readBuf.length() < 19) break;

            readBuf = readBuf.trimmed();
            readBuf.replace("OK", "");
            int i = 0;
            for(i = 0; i < readBuf.length(); i++)
                if(readBuf.at(i) >='0' && readBuf.at(i) <= '9')
                    break;
            if(i > 0) readBuf.remove(0, i);
            if(readBuf.length() >= 15) {
                m_NBState = NBStateSetCOPS;
                QString imei = readBuf.mid(0, 15);
                emit SignalIMEI(imei);
            }
            readBuf.clear();
        }
        break;

    case NBStateClosePSM:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateCloseERDX;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;

    case NBStateCloseERDX:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateClosePM;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;

    case NBStateClosePM:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateSetCOPS;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;

    case NBStateSetCOPS:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateSetCGDCONT;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;
    case NBStateSetCGDCONT:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateSetCGACT;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;
    case NBStateSetCGACT:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateBind;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;
    case NBStateBind:
        rlen = readBuf.indexOf("\r\nOK\r\n");
        if(rlen >= 0) {
            m_NBState = NBStateSend;
            readBuf.remove(0, rlen+6);
        }
        else {
            readBuf.clear();
        }
        break;
    case NBStateSend:
        rlen = readBuf.indexOf("+CSQ: ");
        if(buflen - rlen < 4) break;
        if(rlen >= 0) {
            QString csq = readBuf.mid(rlen+6, 2);
            emit SignalCSQ(csq);
            readBuf.remove(0, rlen+6);
        }

        rlen = readBuf.indexOf("+NNMI: ");
        if(buflen - rlen < 10) break;
        if(rlen > 0) {
            emit SignalUpDown(false);
        }
        readBuf.clear();
        break;
    }
}

size_t SerialThread::GetRecvNum()
{
    //std::lock_guard<std::mutex> lock(m_NumMutex);

    size_t tmp = recvNum;

    return tmp;
}
size_t SerialThread::GetSndNum()
{
    //std::lock_guard<std::mutex> lock(m_NumMutex);

    size_t tmp = sndNum;

    return tmp;
}
void SerialThread::ClrRecvSndNum()
{
    std::lock_guard<std::mutex> lock(m_NumMutex);

    sndNum = 0;
    recvNum = 0;
}

void SerialThread::SendATCmd(const char* buf)
{
    m_SendQStr.clear();
    m_SendQStr.append(buf);

    sndNum += m_SendQStr.length();

    SlotSend(m_SendQStr);
    emit SignalInfo(m_SendQStr);
}

void SerialThread::SlotSend(const QByteArray &data)
{
    m_Port->write(data);
}

bool SerialThread::OpenPort(const QString& name)
{
    std::lock_guard<std::mutex> lock(m_Mutex);

    if(m_IsOpen) {
        ClosePort();
    }
    m_PortInfo = new QSerialPortInfo(name);
    m_Port->setPort(*m_PortInfo);

    m_IsOpen = m_Port->open(QIODevice::ReadWrite);
    if(!m_IsOpen) ClosePort();

    return m_IsOpen;
}

void SerialThread::ClosePort()
{
    if(m_Port->isOpen())
        m_Port->close();

    if(m_PortInfo) {
        delete m_PortInfo;
        m_PortInfo = nullptr;
    }
}

bool SerialThread::IsOpen()
{
    return m_IsOpen;
}

void SerialThread::SlotOpenSerailPort(const QString& name)
{
    OpenPort(name);
}

void SerialThread::SlotCloseSerialPort()
{
    ClosePort();
}

void SerialThread::OpenSerial(const QString& name)
{
    emit this->SignalOpen(name);
}

void SerialThread::CloseSerial()
{
    emit this->SignalClose();
}
